#include "Controls/Control.h"
#include "cinder/gl/gl.h"

namespace cxgui
{
using namespace ci;
using namespace std;

Control::Control()
	: _backColor(0.7f, 0.7f, 0.7f, 1.f),
	_foreColor(0.f, 0.f, 0.f, 1.f),
	_font(Font::getDefault()),
	_margin(0.f, 0.f, 0.f, 0.f),
	_padding(0.f, 0.f, 0.f, 0.f),
	_borderThickness(0.f, 0.f, 0.f, 0.f),
	_borderColor(0.f, 0.f, 0.f, 0.f),
	_verticalAlignment(VerticalAlignment::Center),
	_horizontalAlignment(HorizontalAlignment::Center),
	_isLayoutDirty(true)
{	
}

void Control::Update()
{
}

void Control::Draw()
{
	DrawBackground();
	DrawBorder();
	DrawForeground();
}

void Control::SetParent(shared_ptr<Control> control)
{
	_parent = control;
	OnParentChanged();
}

void Control::SetBackColor(ColorA color)
{
	_backColor = color;
	OnBackColorChanged();
}

void Control::OnParentChanged()
{
}

Control::~Control()
{
}

void Control::OnBackColorChanged()
{
}




void Control::DrawBackground()
{
	gl::color(_backColor);

	gl::drawSolidRect(_clientRectangle);
}

Rectf Control::GetMargin() const
{
	return _margin;
}

Rectf Control::GetPadding() const
{
	return _padding;
}

Rectf Control::GetBorderThickness() const
{
	return _borderThickness;
}

void Control::SetMargin(const Rectf& margin)
{
	_margin = margin;
}

void Control::SetMargin( float left, float top, float right, float bottom )
{
	_margin.set(left, top, right, bottom);
}

void Control::SetMargin( float leftAndRight, float topAndBottom )
{
	_margin.set(leftAndRight, topAndBottom, leftAndRight, topAndBottom);
}

void Control::SetMargin( float all )
{
	_margin.set(all, all, all, all);
}

void Control::SetPadding(const Rectf& padding)
{
	_padding = padding;
}

void Control::SetPadding( float left, float top, float right, float bottom )
{
	_padding.set(left, top, right, bottom);
}

void Control::SetPadding( float leftAndRight, float topAndBottom )
{
	_padding.set(leftAndRight, topAndBottom, leftAndRight, topAndBottom);
}

void Control::SetPadding( float all )
{
	_padding.set(all, all, all, all);
}

void Control::SetBorderThickness(const Rectf& borderThickness)
{
	_borderThickness = borderThickness;
}

void Control::DrawForeground()
{	
}

Vec2f Control::GetPreferedSize()	
{
	return Vec2f(_padding.x1 + _padding.x2,
		_padding.y1 + _padding.y2);
}


void Control::DrawBorder()
{
	if(_borderThickness.getWidth() > 0.f)
	{
		gl::color(_borderColor);
	}
}

VerticalAlignment Control::GetVerticalAlignment() const
{
	return _verticalAlignment;
}

HorizontalAlignment Control::GetHorizontalAlignment() const
{
	return _horizontalAlignment;
}

void Control::SetVerticalAlignment(VerticalAlignment alignment)
{
	if(_verticalAlignment != alignment)
	{
		_verticalAlignment = alignment;
		IsLayoutDirty(true);
	}	
}

void Control::SetHorizontalAlignment(HorizontalAlignment alignment)
{
	if(_horizontalAlignment != alignment) 
	{
		_horizontalAlignment = alignment;	
		IsLayoutDirty(true);
	}	
}


cinder::Rectf Control::GetClientRectangle() const
{
	return _clientRectangle;
}

void Control::SetClientRectangle( const Rectf& rect )
{
	_clientRectangle = CalculateClientArea(rect);	
	OnClientRectangleChanged();
}

cinder::Rectf Control::CalculateClientArea( const Rectf& clientArea )
{
	Rectf finalRect(0.f, 0.f, 0.f, 0.f);
	auto preferedSize = GetPreferedSize();
	switch (_verticalAlignment)
	{
	case cxgui::VerticalAlignment::Top:
		finalRect.y1 = clientArea.y1 + _margin.y1;
		finalRect.y2 = finalRect.y1 + preferedSize.y;		
		break;
	case cxgui::VerticalAlignment::Bottom:
		finalRect.y1 = clientArea.y2 - preferedSize.y - _margin.y2;
		finalRect.y2 = finalRect.y1 + preferedSize.y;
		break;
	case cxgui::VerticalAlignment::Center:
		finalRect.y1 = (clientArea.y1 + (clientArea.getHeight() * 0.5f)) - (preferedSize.y * 0.5f);
		finalRect.y2 = finalRect.y1 + preferedSize.y;
		break;
	case cxgui::VerticalAlignment::Stretch:
		finalRect.y1 = clientArea.y1 + _margin.y1;
		finalRect.y2 = clientArea.y2 - _margin.y2;				
		break;
	default:
		break;
	}

	switch (_horizontalAlignment)
	{
	case cxgui::HorizontalAlignment::Left:
		finalRect.x1 = clientArea.x1 + _margin.x1;		
		finalRect.x2 = finalRect.x1 + preferedSize.x;
		break;
	case cxgui::HorizontalAlignment::Right:
		finalRect.x1 = clientArea.x2 - preferedSize.x - _margin.x2;
		finalRect.x2 = finalRect.x1 + preferedSize.x;
		break;
	case cxgui::HorizontalAlignment::Center:
		finalRect.x1 = (clientArea.x1 + (clientArea.getWidth() * 0.5f)) - (preferedSize.x * 0.5f);
		finalRect.x2 = finalRect.x1 + preferedSize.x;
		break;
	case cxgui::HorizontalAlignment::Stretch:
		finalRect.x1 = clientArea.x1 + _margin.x1;
		finalRect.x2 = clientArea.x2 - _margin.x2;		
		break;
	default:
		break;
	}
	return finalRect;
}


void Control::GrowRectangle(Rectf& outRect, const Rectf& thickness )
{	
	outRect.set(outRect.x1 - thickness.x1,
		outRect.y1 - thickness.y1,
		outRect.x2 + thickness.x2,
		outRect.y2 + thickness.y2);
}

void Control::ShrinkRectangle( Rectf& outRect, const Rectf& thickness )
{
	outRect.set(outRect.x1 + thickness.x1,
		outRect.y1 + thickness.y1,
		outRect.x2 - thickness.x2,
		outRect.y2 - thickness.y2);
}

void Control::OnClientRectangleChanged()
{
	_isLayoutDirty = false;
}

void Control::IsLayoutDirty( bool isDirty )
{
	if(_isLayoutDirty != isDirty)
	{
		_isLayoutDirty = isDirty;
		OnIsLayoutDirtyChanged();
	}
}

bool Control::IsLayoutDirty() const
{
	return _isLayoutDirty;
}

void Control::OnIsLayoutDirtyChanged()
{
}

cinder::Vec2f Control::GetTextSize( const string& text )
{
	auto glyphses = _font.getGlyphs(text);
	auto x = 0.f;
	auto y = 0.f;
	auto box = Rectf(0.f, 0.f, 0.f, 0.f);
	for(auto glyph : glyphses)
	{
		box = _font.getGlyphBoundingBox(glyph);
		x += box.getWidth();		
		if(box.getHeight() > y)
		{
			y = box.getHeight();
		}
	}
	return Vec2f(x, y);
}

void Control::GrowVect( Vec2f& outVect, const Rectf& thickness )
{
	outVect.x += thickness.x1 + thickness.x2;
	outVect.y += thickness.y1 + thickness.y2;
}

}